<template>
  <div class="model-upload-viewer">
    <!-- 上传按钮 -->
    <el-upload
      :action="uploadUrl"
      :show-file-list="false"
      :on-success="handleUploadSuccess"
      :before-upload="beforeUpload"
    >
      <el-button type="primary">上传 .glb 模型</el-button>
    </el-upload>

    <!-- Three.js 渲染容器 -->
    <div
      ref="threeContainer"
      class="canvas-container"
      @click="handleClick"
    ></div>
    <!-- 属性信息浮窗 -->
    <el-card class="info-card" v-if="selectedInfo">
      <div>🧩 <strong>名称：</strong>{{ selectedInfo.name || "未命名" }}</div>
      <div><strong>类型：</strong>{{ selectedInfo.type }}</div>
      <div><strong>材质：</strong>{{ selectedInfo.materialName }}</div>
    </el-card>
  </div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { ElMessage } from "element-plus";

const uploadUrl = "http://localhost:9999/api/upload-image"; // 可替换成分片接口

const threeContainer = ref(null);
let scene, camera, renderer, controls;
const raycaster = new THREE.Raycaster(); //用于计算鼠标在 3D 空间中与物体的交点。
const mouse = new THREE.Vector2(); //存储鼠标在屏幕上的归一化坐标（范围：[-1, 1]）。

const selectedInfo = ref(null);
let currentHighlighted = null; // 当前高亮 mesh
let originalMaterial = null; // 记录原始材质

// 初始化 Three.js 场景
onMounted(() => {
  scene = new THREE.Scene();

  // 相机
  camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  camera.position.set(2, 2, 4);

  // 渲染器
  // 渲染器设置中启用阴影和色调映射
  renderer = new THREE.WebGLRenderer({
    antialias: true,
    // alpha: true, // 启用透明背景
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  // renderer.shadowMap.enabled = true; // 启用阴影  坑
  renderer.toneMapping = THREE.ACESFilmicToneMapping; // 高级色调映射
  renderer.toneMappingExposure = 1.2; // 增加曝光值

  threeContainer.value.appendChild(renderer.domElement);

  // 光源
  scene.add(new THREE.AmbientLight(0xffffff, 1.5)); //环境光
  const dirLight = new THREE.DirectionalLight(0xffffff, 2.5); //方向光
  dirLight.position.set(5, 15, 10);
  dirLight.castShadow = true; // 启用阴影投射
  scene.add(dirLight);

  // 添加半球光 - 模拟天空和地面反射
  const hemisphereLight = new THREE.HemisphereLight(
    0xffffdd, // 更亮的天空色
    0x204080, // 更亮的地面色
    1.8 // 增强强度
  );
  scene.add(hemisphereLight);

  // 添加点光源 - 补充暗部区域
  const pointLight = new THREE.PointLight(0xffffff, 2.0, 100);
  pointLight.position.set(-5, 8, -5); // 提高位置
  pointLight.castShadow = true; // 启用阴影
  scene.add(pointLight);

  // 添加辅助光源 - 背光
  const backLight = new THREE.DirectionalLight(0xffffff, 1.2);
  backLight.position.set(-5, 5, -10);
  backLight.castShadow = true;
  scene.add(backLight);

  // 控制器
  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;

  animate();

  handleUploadSuccess({
    url: "http://localhost:3000/uploads/1753931582746_5m4xnph1f04.glb",
  });
});

// 渲染循环
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

// 上传前校验格式
const beforeUpload = (file) => {
  if (!file.name.endsWith(".glb")) {
    ElMessage.warning("只支持 .glb 格式文件");
    return false;
  }
  return true;
};

// 上传成功后加载模型
const handleUploadSuccess = (res) => {
  const modelUrl = `${res.url}`; // 拼接完整 URL
  loadModel(modelUrl);
};

// 加载 .glb 模型
function loadModel(url) {
  const loader = new GLTFLoader();
  loader.load(
    url,
    (gltf) => {
      // 清空之前模型
      scene.children = scene.children.filter((obj) => !obj.isGroup);
      const model = gltf.scene;
      scene.add(model);

      // 自动居中和缩放
      const box = new THREE.Box3().setFromObject(model);
      const center = box.getCenter(new THREE.Vector3());
      const size = box.getSize(new THREE.Vector3());
      model.position.sub(center);

      const maxDim = Math.max(size.x, size.y, size.z);
      camera.position.set(0, maxDim, maxDim * 2);
      camera.lookAt(0, 0, 0);
      controls.update();
    },
    undefined,
    (err) => {
      console.error("❌ 模型加载失败", err);
      ElMessage.error("模型加载失败");
    }
  );
}
// 点击事件拾取模型部件
function handleClick(event) {
  const rect = renderer.domElement.getBoundingClientRect();
  mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
  mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; //将屏幕像素坐标转换为 Three.js 的标准化设备坐标（NDC）。

  //从相机向鼠标位置发射射线。
  //检测射线与场景中所有物体的交点（true 表示递归检测子物体）。
  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(scene.children, true);

  if (intersects.length > 0) {
    const mesh = intersects[0].object; // 获取第一个交点对应的物体

    // 取消上一个高亮
    if (currentHighlighted && originalMaterial) {
      currentHighlighted.material = originalMaterial;  // 恢复原始材质
    }

    // 高亮当前部件
    originalMaterial = mesh.material; // 备份原始材质
    currentHighlighted = mesh; // 记录当前物体
    mesh.material = mesh.material.clone(); // 克隆材质（避免影响其他物体）
    mesh.material.color.set(0xff6600); // 设置新颜色（橙色）

    // 展示属性信息
    selectedInfo.value = {
      name: mesh.name,
      type: mesh.type,
      materialName: mesh.material.name || "默认材质",
    };
  }
}
</script>

<style scoped>
.model-upload-viewer {
  width: 100%;
  height: 100vh;
  overflow: hidden;
  padding: 16px;
}
.canvas-container {
  width: 100%;
  height: calc(100vh - 64px); /* 留出上传按钮空间 */
  border: 1px solid #ccc;
  margin-top: 16px;
}
.info-card {
  position: absolute;
  top: 160px;
  right: 24px;
  width: 240px;
  z-index: 10;
  background: white;
}
</style>
